package com.dualsportmaps.android.views;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

import net.osmand.osm.LatLon;
import net.osmand.plus.OsmandSettings;
import net.osmand.plus.views.OsmandMapLayer;
import net.osmand.plus.views.OsmandMapTileView;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.text.format.DateFormat;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import android.widget.Toast;

import com.dualsportmaps.android.DsmConnectionFactory;
import com.dualsportmaps.android.DsmResourceManager;
import com.dualsportmaps.android.EncodedPolyline;
import com.dualsportmaps.android.SimpleTrackpoint;
import com.dualsportmaps.android.model.DsmMapWpt;
import com.dualsportmaps.android.shared.model.DsmAppUserLocationProxy;
import com.google.web.bindery.requestfactory.shared.Receiver;
import com.google.web.bindery.requestfactory.shared.ServerFailure;

public class DsmAppUserLocationLayer implements OsmandMapLayer {

	private OsmandMapTileView view;
	private DisplayMetrics dm;
	private Path path;
	private List<DsmMapWpt> visibleWaypoints = null;
	private EncodedPolyline historyTrack = null;
	private String dsmAppUserId = null;
	private static java.text.DateFormat df = DateFormat.getMediumDateFormat(DsmResourceManager.getApplicationContext());
	private static java.text.DateFormat tf = DateFormat.getTimeFormat(DsmResourceManager.getApplicationContext());
	
	public void setDsmAppUserId(String dsmAppUserId) {
		this.dsmAppUserId = dsmAppUserId;
		visibleWaypoints = null;
		historyTrack = null;
		view.refreshMap();
	}
	
	private void initUI() {
		path = new Path();
		dm = new DisplayMetrics();
		WindowManager wmgr = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
		wmgr.getDefaultDisplay().getMetrics(dm);
		visibleWaypoints = null;
		historyTrack = null;
		this.dsmAppUserId = null;
	}


	@Override
	public void initLayer(OsmandMapTileView view) {
		this.view = view;
		initUI();
	}

	private synchronized void loadData() {
		if(OsmandSettings.isInternetConnectionAvailable(DsmResourceManager.getApplicationContext()) && !DsmResourceManager.loadingUserLocations) {
			DsmResourceManager.loadingUserLocations = true;
			DsmResourceManager.getMapActivity().runOnUiThread(new Runnable() {
				@Override
				public void run() {
					Toast.makeText(view.getContext(),  "Loading user locations.", Toast.LENGTH_SHORT).show();	
				}});
			new Thread(new Runnable(){
				@Override
				public void run() {
					try {
						DsmConnectionFactory.authenticateDsmAppUser();
						if(dsmAppUserId == null) {
							DsmConnectionFactory.getDsmRequestFactory().databaseProceduresRequest().getDsmAppUserLocationCurrent().fire(new Receiver<List<DsmAppUserLocationProxy>>() {
		
								@Override
								public void onFailure(ServerFailure error) {
									DsmResourceManager.loadingUserLocations = false;
									Toast.makeText(view.getContext(),  "User Location download failure.", Toast.LENGTH_LONG).show();
								}
								
								@Override
								public void onSuccess(List<DsmAppUserLocationProxy> response) {
									visibleWaypoints = new ArrayList<DsmMapWpt>();
									for(DsmAppUserLocationProxy l : response) {
										DsmMapWpt loc = new DsmMapWpt();
										loc.setDsmMapWptName(l.getDsmAppUserHandle());
										loc.setDsmMapWptId(l.getDsmAppUserId());
										try {
											java.util.Date dt = (java.util.Date) new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a Z").parse(l.getDsmAppUserLocationTS() +  " +0000");
											loc.setDsmMapWptDesc(df.format(dt) + " " + tf.format(dt));
										} catch (Exception e) {
											e.printStackTrace();
											loc.setDsmMapWptDesc(l.getDsmAppUserLocationTS() + " UTC");
										}
										loc.setDsmMapWptIcon("Bike Trail");
										loc.setDsmMapWptLat(l.getDsmAppUserLocationLat());
										loc.setDsmMapWptLng(l.getDsmAppUserLocationLng());
										visibleWaypoints.add(loc);
									}
									view.refreshMap();
									DsmResourceManager.loadingUserLocations = false;
								}
							});
						} else {
							DsmConnectionFactory.getDsmRequestFactory().databaseProceduresRequest().getDsmAppUserLocationHistory(dsmAppUserId).fire(new Receiver<List<DsmAppUserLocationProxy>>() {
		
								@Override
								public void onFailure(ServerFailure error) {
									DsmResourceManager.loadingUserLocations = false;
									Toast.makeText(view.getContext(),  "User History download failure.\n"+error.getMessage(), Toast.LENGTH_LONG).show();
								}
								
								@Override
								public void onSuccess(List<DsmAppUserLocationProxy> response) {
									visibleWaypoints = new ArrayList<DsmMapWpt>();
									List<SimpleTrackpoint> trackPoints = new ArrayList<SimpleTrackpoint>();
									int x = 0;
									for(DsmAppUserLocationProxy l : response) {
										DsmMapWpt loc = new DsmMapWpt();
										loc.setDsmMapWptName(l.getDsmAppUserHandle());
										try {
											java.util.Date dt = (java.util.Date) new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a Z").parse(l.getDsmAppUserLocationTS() +  " +0000");
											loc.setDsmMapWptDesc(df.format(dt) + " " + tf.format(dt));
										} catch (Exception e) {
											e.printStackTrace();
											loc.setDsmMapWptDesc(l.getDsmAppUserLocationTS() + " UTC");
										}
										if(x == 0)
											loc.setDsmMapWptIcon("Bike Trail");
										else 
											loc.setDsmMapWptIcon("City (Small)");
										loc.setDsmMapWptLat(l.getDsmAppUserLocationLat());
										loc.setDsmMapWptLng(l.getDsmAppUserLocationLng());
										trackPoints.add(new SimpleTrackpoint((float)l.getDsmAppUserLocationLat(),(float)l.getDsmAppUserLocationLng()));
										if(x == 0 || x % 10 == 0 || response.size()+1 == x)
											visibleWaypoints.add(loc);
										x++;
									}
									historyTrack = new EncodedPolyline(trackPoints);
									DsmResourceManager.getMapActivity().setDsmMapDownloadViewable("User History", visibleWaypoints.get(0).getDsmMapWptLat(), visibleWaypoints.get(0).getDsmMapWptLng(), historyTrack.bound1Lat, historyTrack.bound1Lng, historyTrack.bound2Lat, historyTrack.bound2Lng);
									DsmResourceManager.getMapActivity().runOnUiThread(new Runnable() {
										@Override
										public void run() {
											SharedPreferences settings = OsmandSettings.getPrefs(DsmResourceManager.getApplicationContext());
											LatLon latLon = OsmandSettings.getAndClearMapLocationToShow(settings);
											LatLon cur = new LatLon(view.getLatitude(), view.getLongitude());
											view.refreshMap();
											if (latLon != null && !latLon.equals(cur)) {
												view.getAnimatedDraggingThread().startMoving(cur.getLatitude(), cur.getLongitude(), latLon.getLatitude(),
														latLon.getLongitude(), view.getZoom(), OsmandSettings.getMapZoomToShow(settings), view.getSourceTileSize(),
														view.getRotate(), true);
											}	
										}});
									DsmResourceManager.loadingUserLocations = false;
								}
							});
						}
					} catch (Throwable e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}).start();
		}
	}
	
	@Override
	public void onDraw(final Canvas canvas, final RectF latlonRect, final boolean nightMode) {
		if(visibleWaypoints == null) {
			loadData();
		}
		if(historyTrack != null) {
			canvas.save();
			if(view.showPerspective()) 
				view.addPerspective(canvas);
			if(view.getRotate() != 0)
				canvas.rotate(view.getRotate(), view.getCenterPointX(), view.getCenterPointY());
			Paint paint = getTrackPaint();
			path.rewind();
						
			if(trackOnScreen(new RectF(historyTrack.bound1Lng,historyTrack.bound1Lat,historyTrack.bound2Lng,historyTrack.bound2Lat),latlonRect)) {
				if(historyTrack.hasTrackPointsInRect(view.getZoom(),latlonRect)) {
					List<SimpleTrackpoint> l = historyTrack.getTrackPoints(view.getZoom());
					path.rewind();
					int startIndex = -1;							
					for (int i = 0; i < l.size(); i++) {
						SimpleTrackpoint ls = l.get(i);
						if (startIndex == -1) {
							if (ls.getLatitude() >= latlonRect.bottom && ls.getLatitude() <= latlonRect.top  && ls.getLongitude() >= latlonRect.left 
									&& ls.getLongitude() <= latlonRect.right ) {
								startIndex = i > 0 ? i - 1 : i;
							}
						} else if (!(latlonRect.left <= ls.getLongitude() + 0.03 && ls.getLongitude() - 0.03 <= latlonRect.right
								&& latlonRect.bottom <= ls.getLatitude() + 0.03 && ls.getLatitude() - 0.03 <= latlonRect.top)) {
							drawSegment(canvas, l, startIndex, i, paint);
							startIndex = -1;
						}
					}
					if (startIndex != -1) {
						drawSegment(canvas, l, startIndex, l.size() - 1, paint);
					}
				}
			}
			canvas.restore();
		}
		if(visibleWaypoints != null) {
			int shrinkageSize = 36; //24 + (view.getZoom()-10)*2;					
			try {
				for (DsmMapWpt wpt : new ArrayList<DsmMapWpt>(visibleWaypoints)) {
					if(trackOnScreen(new RectF(wpt.getDsmMapWptLng().floatValue(),wpt.getDsmMapWptLat().floatValue(),wpt.getDsmMapWptLng().floatValue(),wpt.getDsmMapWptLat().floatValue()),latlonRect)) {
						int[] ps = view.getRotatedMapPoints(wpt.getDsmMapWptLat(),wpt.getDsmMapWptLng());
						int x = ps[0]-shrinkageSize/2;
						int y = ps[1]-shrinkageSize/2;
						
						try {
							canvas.drawBitmap(DsmResourceManager.getScaledIcon(wpt.getDsmMapWptIcon(),shrinkageSize),x,y,null);
						} catch (Throwable e) {
							e.printStackTrace();
						}
					}
				}
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private void drawSegment(Canvas canvas, List<SimpleTrackpoint> l, int startIndex, int endIndex, Paint paint) {
		double startLng = l.get(startIndex).getLongitude();
		double startLat = l.get(startIndex).getLatitude();
		int px = view.getMapXForPoint(startLng);
		int py = view.getMapYForPoint(startLat);
		path.moveTo(px,py);
		for (int i = startIndex + 1; i <= endIndex; i++) {
			SimpleTrackpoint p = l.get(i);
			int x = view.getMapXForPoint(p.getLongitude());
			int y = view.getMapYForPoint(p.getLatitude());
			path.lineTo(x,y);
		}
		canvas.drawPath(path, paint);
	}
	
	private Paint getTrackPaint() {
		int width = 3;
		int c = Color.parseColor("blue");
		if(view.getZoom() > 16)
			width = 12;
		else if(view.getZoom() > 14)
			width = 8;
		else if(view.getZoom() > 11)
			width = 6;
		else if(view.getZoom() > 8)
			width = 4;
		else
			width = 2;
		Paint paint = new Paint();
		paint.setColor(c);	
		paint.setAlpha(180);
		paint.setStyle(Style.STROKE);
		paint.setStrokeWidth(width);
		paint.setStrokeCap(Cap.ROUND);
		paint.setStrokeJoin(Join.ROUND);
		return paint;
	}
	
	public boolean trackOnScreen(RectF trackbox, RectF latLonBounds) {
		RectF parentBox = new RectF(latLonBounds);
		trackbox.sort();
		parentBox.sort();
		if(RectF.intersects(parentBox, trackbox)) {
			return true;
		}
		if(new RectF(latLonBounds).contains(new RectF(trackbox))) {
			return true;
		}
		if(new RectF(trackbox).contains(new RectF(latLonBounds))) {
			return true;		
		}
		return false;
	}
	
	public DsmMapWpt getWaypointFromPoint(PointF point){
		DsmMapWpt result = null;
		if(visibleWaypoints == null)
			return null;
		if (visibleWaypoints.size() > 0) {
			int ex = (int) point.x;
			int ey = (int) point.y;
			int radius = getRadiusPoi(view.getZoom()) * 3 / 2;
			try {
				for (DsmMapWpt n : visibleWaypoints) {
					int x = view.getRotatedMapXForPoint(n.getDsmMapWptLat(), n.getDsmMapWptLng());
					int y = view.getRotatedMapYForPoint(n.getDsmMapWptLat(), n.getDsmMapWptLng());
					if (Math.abs(x - ex) <= radius && Math.abs(y - ey) <= radius) {
						radius = Math.max(Math.abs(x - ex), Math.abs(y - ey));
						result = n;
					}
				}
			} catch (IndexOutOfBoundsException e) {
				// that's really rare case, but is much efficient than introduce synchronized block
			}
		}
		return result;
	}
	
	private int getRadiusPoi(int zoom){
		int r = 0;
		if(zoom < 10){
			r = 8;
		} else if(zoom <= 15){
			r = 10;
		} else if(zoom == 16){
			r = 14;
		} else if(zoom == 17){
			r = 16;
		} else {
			r = 18;
		}
		return (int) (r * dm.density);
	}	
	
	@Override
	public void destroyLayer() {
		view = null;
		visibleWaypoints = null;
		historyTrack = null;
		dsmAppUserId = null;
	}

	@Override
	public boolean onTouchEvent(PointF point) {
		final DsmMapWpt n = getWaypointFromPoint(point);
		if(n != null){
			try {
				AlertDialog.Builder dialog = new AlertDialog.Builder(view.getContext());
				dialog.setIcon(new BitmapDrawable(DsmResourceManager.getScaledIcon(n.getDsmMapWptIcon(),40)));
				
				dialog.setTitle("User Location");
				String wptinfo = n.getDsmMapWptName();
				if(n.getDsmMapWptDesc() != null)
					if(!n.getDsmMapWptDesc().equals(wptinfo))
						wptinfo = wptinfo + "\n" + n.getDsmMapWptDesc();
				dialog.setMessage(wptinfo);
				dialog.setPositiveButton("Friend History", new DialogInterface.OnClickListener(){

					@Override
					public void onClick(DialogInterface dialog, int which) {
						setDsmAppUserId(n.getDsmMapWptId());
					}});
				dialog.setNeutralButton("Location Options", new DialogInterface.OnClickListener(){

					@Override
					public void onClick(DialogInterface dialog, int which) {
						DsmResourceManager.getMapActivity().contextMenuPoint(n.getDsmMapWptLat(), n.getDsmMapWptLng());
					}});
				final AlertDialog d = dialog.create();
				d.show();
				new Thread(new Runnable() {

					@Override
					public void run() {
						try {
							Thread.sleep(3000);
							d.dismiss();
						} catch (InterruptedException e) {
						}
					}}).start();			
				return true;
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
		return false;
	}

	@Override
	public boolean onLongPressEvent(PointF point) {
		return false;
	}

	@Override
	public boolean drawInScreenPixels() {
		return true;
	}

}
